//Copyright 2010 Balazs Keri, Imre Keri
//
//This file is part of FireworksCreator.
//
//FireworksCreator is free software: you can redistribute it and/or modify
//it under the terms of the GNU General Public License as published by
//the Free Software Foundation, either version 3 of the License, or
//(at your option) any later version.
//
//FireworksCreator is distributed in the hope that it will be useful,
//but WITHOUT ANY WARRANTY; without even the implied warranty of
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//GNU General Public License for more details.
//
//You should have received a copy of the GNU General Public License
//along with FireworksCreator.  If not, see <http://www.gnu.org/licenses/>.
//
#include <QtDebug>
#include <QTextStream>
#include <model/particleshooter.h>
#include <model/particletemplate.h>
#include <model/emittertemplate.h>
#include <model/randomcolor.h>
#include <model/randomdouble.h>
#include <model/randomposition.h>
#include <model/root.h>
#include "attrevent.h"
#include "simpletextview.h"
using namespace model;
//
SimpleTextView::SimpleTextView(QWidget *parent) : QWidget(parent) {
   ui.setupUi(this);
   connect(ui.createButton, SIGNAL(clicked()), SLOT(parseAndCreate()));
}
SimpleTextView::~SimpleTextView() {
}
void SimpleTextView::handle_set_attr_group() {
   if (get_document() != 0) {
      setEnabled(true);
   } else {
      setEnabled(false);
   }
}
void SimpleTextView::handle_attr_event(AttrEvent *evnt) {
}
//
//
static inline bool keywordCompare(QString keyw, QString to) {
   return keyw.compare(to, Qt::CaseInsensitive) == 0;
}
inline bool SimpleTextView::keywordIs(QString kw) {
   return keywordCompare(currentKeyword, kw);
}
static QTextStream &operator >>(QTextStream& ts, RandomColor &rc) {
   QString keyw; ts >> keyw;
   if (keywordCompare(keyw, "constant")) {
      osg::Vec4 cval;
      ts >> cval.r() >> cval.g() >> cval.b() >> cval.a();
      rc=RandomColor(new RandomColor::Constant(cval));
   } else {
      qDebug() << "unknown RandomColor type: " << keyw << endl;
   }
   return ts;
}
static QTextStream &operator >>(QTextStream& ts, RandomDouble &rd) {
   QString keyw;
   ts >> keyw;
   if (keywordCompare(keyw, "constant")) {
      double val; ts >> val;
      rd=RandomDouble(new RandomDouble::Constant(val));
   } else if (keywordCompare(keyw, "interval")) {
      double mn, mx; ts >> mn >> mx;
      rd=RandomDouble(new RandomDouble::Interval(mn, mx));
   } else {
      qDebug() << "unknown RandomDouble type: " << keyw << endl;
   }
   return ts;
}
static QTextStream &operator >>(QTextStream &ts, RandomPosition &rp) {
   QString keyw; ts >> keyw;
   if (keywordCompare(keyw, "constant")) {
      osg::Vec3 pos;
      ts >> pos.x() >> pos.y() >> pos.z();
      rp=RandomPosition(new RandomPosition::Constant(pos));
   } else if (keywordCompare(keyw, "box")) {
      osg::Vec3 mn, mx;
      ts >> mn.x() >> mn.y() >> mn.z() >> mx.x() >> mx.y() >> mx.z();
      rp=RandomPosition(new RandomPosition::Box(mn, mx));
   } else {
      qDebug() << "unknown RandomPosition type: " << keyw << endl;
   }
   return ts;
}
bool SimpleTextView::parse1() {
   QString txt = ui.txtEdit->toPlainText();
   QTextStream src(&txt, QIODevice::ReadOnly);
   Document *doc = get_document();
   while (!src.atEnd()) {
      QString value;
      src >> currentKeyword;
      if (keywordIs("particletemplate")) {
         src >> value;
         ParticleTemplate *pt = new ParticleTemplate();
         pt->set_id (doc->generate_id());
         pt->set_name(value);
         doc->get_root()->particle_templates.insert(pt);
         doc->map_object(pt);
         nameMap[value] = pt;
      } else if (keywordIs("emittertemplate")) {
         src >> value;
         EmitterTemplate *et = new EmitterTemplate();
         et->set_id (doc->generate_id());
         et->set_name(value);
         doc->get_root()->emitter_templates.insert(et);
         doc->map_object(et);
         nameMap[value] = et;
      } else if (keywordIs("particleshooter")) {
         src >> value;
         ParticleShooter *ps = new ParticleShooter();
         ps->set_id (doc->generate_id());
         ps->set_name(value);
         doc->get_root()->shooters.insert(ps);
         doc->map_object(ps);
         nameMap[value] = ps;
      }
   }
   return true;
}
bool SimpleTextView::parseTimeMarks(QTextStream &src, model::ParticleTemplate *pt) {
   while (!src.atEnd()) {
      src >> currentKeyword;
      if (keywordIs("end")) {
         return true;
      }
      else if (!keywordIs("mark")) {
         qDebug() << "unknown keyword: " << currentKeyword << endl;
         return false;
      }
      ParticleTemplate::TimeMark mark;
      QString name; src >> name;
      src >> currentKeyword;
      if (keywordIs("from_start"))
         mark.mode = ParticleTemplate::TimeMark::from_start;
      else if (keywordIs("from_end"))
         mark.mode = ParticleTemplate::TimeMark::from_end;
      else if (keywordIs("from_other")) {
         mark.mode = ParticleTemplate::TimeMark::from_other;
         src >> mark.other;
         if (!pt->time_marks.contains(mark.other)) {
            qDebug() << "time mark '" << mark.other << "' not found" << endl;
            return false;
         }
      }
      else {
         qDebug() << "unknown time mark mode: " << currentKeyword << endl;
         return false;
      }
      src >> mark.time;
      pt->time_marks[name] = mark;
   }
   return false;
}
bool SimpleTextView::parseEmitterBlocks(QTextStream &src, ParticleTemplate *pt) {
   while (!src.atEnd()) {
      src >> currentKeyword;
      if (keywordIs("end"))
         return true;
      else if (!keywordIs("block")) {
         qDebug() << "unknown keyword: " << currentKeyword << endl;
         return false;
      }
      ParticleTemplate::EmitterBlock block;
      src >> block.name;
      QString etname; src >> etname;
      if (!nameMap.contains(etname)) {
         qDebug() << "emitter template with name '" << etname << "' not found" << endl;
         return false;
      }
      EmitterTemplate *et = qobject_cast<EmitterTemplate*>(nameMap[etname]);
      block.emitter = et;
      src >> currentKeyword;
      if (keywordIs("once")) {
         src >> block.time1;
         if (!pt->time_marks.contains(block.time1)) {
            qDebug() << "time mark '" << block.time1 << "' not found" << endl;
            return false;
         }
      } else if (keywordIs("rep")) {
         src >> block.time1 >> block.time2 >> block.interval_min >> block.interval_max;
         if (!pt->time_marks.contains(block.time1)) {
            qDebug() << "time mark '" << block.time1 << "' not found" << endl;
            return false;
         }
         if (!pt->time_marks.contains(block.time2)) {
            qDebug() << "time mark '" << block.time2 << "' not found" << endl;
            return false;
         }
      } else {
         qDebug() << "unknown keyword: " << currentKeyword << endl;
         return false;
      }
      pt->emitters.append(block);
   }
   return false;
}
bool SimpleTextView::parseColorControlPoints(QTextStream &src, model::ParticleTemplate *pt) {
   while (!src.atEnd()) {
      src >> currentKeyword;
      if (keywordIs("point")) {
         ParticleTemplate::ColorControlPoint point;
         src >> point.time;
         if (point.time == "\"\"")
            point.time.clear();
         else if (!pt->time_marks.contains(point.time)) {
            qDebug() << "time mark '" << point.time << "' not found" << endl;
            return false;
         }
         src >> point.color;
         pt->color_control_points.append(point);
      } else if (keywordIs("end")) {
         return true;
      } else {
         qDebug() << "unknown keyword: " << currentKeyword << endl;
         return false;
      }
   }
   return false;
}
bool SimpleTextView::parseFloatControlPoints(QTextStream &src, model::ParticleTemplate *pt, QList<model::ParticleTemplate::FloatControlPoint> &pointlist) {
   while (!src.atEnd()) {
      src >> currentKeyword;
      if (keywordIs("point")) {
         ParticleTemplate::FloatControlPoint point;
         src >> point.time;
         if (point.time == "\"\"")
            point.time.clear();
         else if (!pt->time_marks.contains(point.time)) {
            qDebug() << "time mark '" << point.time << "' not found" << endl;
            return false;
         }
         src >> point.value;
         pointlist.append(point);
      } else if (keywordIs("end")) {
         return true;
      } else {
         qDebug() << "unknown keyword: " << currentKeyword << endl;
         return false;
      }
   }
   return false;
}
bool SimpleTextView::parseParticleTemplate(QTextStream &src, ParticleTemplate *pt) {
   while (!src.atEnd()) {
      src >> currentKeyword;
      if (keywordIs("visible")) {
         int yes; src >> yes;
         pt->set_visible(yes ? true : false);
      } else if (keywordIs("life_time")) {
         RandomDouble lt; src >> lt;
         pt->set_life_time(lt);
      } else if (keywordIs("density")) {
         float d; src >> d;
         pt->set_density(d);
      } else if (keywordIs("gravitation_enabled")) {
         int yes; src >> yes;
         pt->set_gravitation_enabled(yes ? true : false);
      } else if (keywordIs("friction_enabled")) {
         int yes; src >> yes;
         pt->set_friction_enabled(yes ? true : false);
      } else if (keywordIs("shape")) {
         ParticleTemplate::Shape sh;
         src >> currentKeyword;
         if (keywordIs("point"))
            sh = ParticleTemplate::shape_point;
         else if (keywordIs("quad"))
            sh = ParticleTemplate::shape_quad;
         else if (keywordIs("quad_trianglestrip"))
            sh = ParticleTemplate::shape_quad_trianglestrip;
         else if (keywordIs("hexagon"))
            sh = ParticleTemplate::shape_hexagon;
         else {
            qDebug() << "unknown shape type: " << currentKeyword << endl;
            return false;
         }
         pt->set_shape(sh);
      } else if (keywordIs("texture_file")) {
         QString fn; src >> fn;
         pt->set_texture_file(fn);
      } else if (keywordIs("tile")) {
         unsigned int ts, tt; src >> ts >> tt;
         pt->set_tile(ts, tt);
      } else if (keywordIs("num_tiles")) {
         float nt; src >> nt;
         pt->set_num_tiles(nt);
      } else if (keywordIs("tile_speed_mode")) {
         ParticleTemplate::TileSpeedMode md;
         src >> currentKeyword;
         if (keywordIs("relative"))
            md = ParticleTemplate::tile_speed_relative;
         else if (keywordIs("absolute"))
            md = ParticleTemplate::tile_speed_absolute;
         else {
            qDebug() << "unknown tile speed mode: " << currentKeyword << endl;
            return false;
         }
         pt->set_tile_speed_mode(md);
      } else if (keywordIs("time_marks")) {
         if (!parseTimeMarks(src, pt))
            return false;
      } else if (keywordIs("emitter_blocks")) {
         if (!parseEmitterBlocks(src, pt))
            return false;
      } else if (keywordIs("color_cp")) {
         if (!parseColorControlPoints(src, pt))
            return false;
      } else if (keywordIs("alpha_cp")) {
         if (!parseFloatControlPoints(src, pt, pt->alpha_control_points))
            return false;
      } else if (keywordIs("size_cp")) {
         if (!parseFloatControlPoints(src, pt, pt->size_control_points))
            return false;
      } else if (keywordIs("radius_cp")) {
         if (!parseFloatControlPoints(src, pt, pt->radius_control_points))
            return false;
      } else if (keywordIs("end")) {
         return true;
      } else {
         return false;
      }
   }
   return false;
}
bool SimpleTextView::parseEmitterBlock(QTextStream &src, EmitterTemplate::EmitterBlock &eblock) {
   while (!src.atEnd()) {
      src >> currentKeyword;
      if (keywordIs("amount")) {
         src >> eblock.amount;
      } else if (keywordIs("scale")) {
         src >> eblock.scale;
      } else if (keywordIs("synchronized")) {
         unsigned int yes; src >> yes;
         eblock.synchronized = yes != 0;
      } else if (keywordIs("position")) {
         src >> eblock.position;
      } else if (keywordIs("angle")) {
         src >> eblock.angle;
      } else if (keywordIs("angular_velocity")) {
         src >> eblock.angular_velocity;
      } else if (keywordIs("particle")) {
         QString ptname; src >> ptname;
         if (!nameMap.contains(ptname)) {
            qDebug() << "particle template with name '" << ptname << "' not found" << endl;
            return false;
         }
         ParticleTemplate *pt = qobject_cast<ParticleTemplate*>(nameMap[ptname]);
         eblock.particle = pt;
      } else if (keywordIs("emitter")) {
         QString etname; src >> etname;
         if (!nameMap.contains(etname)) {
            qDebug() << "emitter template with name '" << etname << "' not found" << endl;
            return false;
         }
         EmitterTemplate *et = qobject_cast<EmitterTemplate*>(nameMap[etname]);
         eblock.emitter = et;
      } else if (keywordIs("end")) {
         return true;
      } else {
         qDebug() << "unknown keyword: " << currentKeyword << endl;
         return false;
      }
   }
   qDebug() << "unexpected end of input" << endl;
   return false;
}
bool SimpleTextView::parseEmitterBlocks(QTextStream &src, EmitterTemplate *et) {
   while (!src.atEnd()) {
      src >> currentKeyword;
      if (keywordIs("block")) {
         EmitterTemplate::EmitterBlock block;
         src >> block.name;
         if (!parseEmitterBlock(src, block))
            return false;
         et->emitter_blocks.append(block);
      } else if (keywordIs("end")) {
         return true;
      } else {
         qDebug() << "unknown keyword: " << currentKeyword << endl;
         return false;
      }
   }
   qDebug() << "unexpected end of input" << endl;
   return false;
}
bool SimpleTextView::parseEmitterTemplate(QTextStream &src, EmitterTemplate *et) {
   while (!src.atEnd()) {
      src >> currentKeyword;
      if (keywordIs("blocks")) {
         if (!parseEmitterBlocks(src, et))
            return false;
      } else if (keywordIs("end")) {
         return true;
      } else {
         qDebug() << "unknown keyword: " << currentKeyword << endl;
         return false;
      }
   }
   qDebug() << "unexpected end of input" << endl;
   return false;
}
bool SimpleTextView::parseParticleShooter(QTextStream &src, ParticleShooter *ps) {
   while (!src.atEnd()) {
      src >> currentKeyword;
      if (keywordIs("time")) {
         double tim; src >> tim;
         ps->set_time(tim);
      } else if (keywordIs("position")) {
         osg::Vec3 v; src >> v.x() >> v.y() >> v.z();
         ps->set_position(v);
      } else if (keywordIs("angle")) {
         osg::Vec3 v; src >> v.x() >> v.y() >> v.z();
         ps->set_angle(v);
      } else if (keywordIs("velocity")) {
         osg::Vec3 v; src >> v.x() >> v.y() >> v.z();
         ps->set_velocity(v);
      } else if (keywordIs("angular_velocity")) {
         osg::Vec3 v; src >> v.x() >> v.y() >> v.z();
         ps->set_angular_velocity(v);
      } else if (keywordIs("particle_template")) {
         QString ptname; src >> ptname;
         if (!nameMap.contains(ptname)) {
            qDebug() << "particle template with name '" << ptname << "' not found" << endl;
            return false;
         }
         ParticleTemplate *pt = qobject_cast<ParticleTemplate*>(nameMap[ptname]);
         ps->set_particle_template(pt);
      } else if (keywordIs("end")) {
         return true;
      } else {
         qDebug() << "unknown keyword: " << currentKeyword << endl;
         return false;
      }
   }
   qDebug() << "unexpected end of input" << endl;
   return false;
}
bool SimpleTextView::parse2() {
   QString txt = ui.txtEdit->toPlainText();
   QTextStream src(&txt, QIODevice::ReadOnly);
   //Document *doc = get_document();
   while (!src.atEnd()) {
      src >> currentKeyword;
      if (keywordIs("particletemplate")) {
         QString name;
         src >> name;
         ParticleTemplate *pt = qobject_cast<ParticleTemplate*>(nameMap[name]);
         if (!pt || !parseParticleTemplate(src, pt))
            return false;
      } else if (keywordIs("emittertemplate")) {
         QString value;
         src >> value;
         EmitterTemplate *et = qobject_cast<EmitterTemplate*>(nameMap[value]);
         if (!et || !parseEmitterTemplate(src, et))
            return false;
      } else if (keywordIs("particleshooter")) {
         QString value;
         src >> value;
         ParticleShooter *ps = qobject_cast<ParticleShooter*>(nameMap[value]);
         if (!ps || !parseParticleShooter(src, ps))
            return false;
      }
   }
   return true;
}
void SimpleTextView::parseAndCreate() {
   Document *doc = get_document();
   if (!doc)
      return;
   doc->delete_contents();
   nameMap.clear();
   bool ok = false;
   if (parse1()) {
      if (parse2()) ok = true;
   }
   if (!ok)
      qDebug() << "error" << endl;
   nameMap.clear();
   //DocEvents::SomethingChanged evnt(doc);
   //doc->emit_data_changed(&evnt);
}
